package cc.rengu.oltp.utility.util;

import cc.rengu.utility.base.PlaceholderResolver;
import cc.rengu.utility.cache.UnifiedCache;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * XML配置文件操作工具类
 * Created by 王成 on 2017/11/13.
 */
public class XmlConfigUtil {
    private static final RgLogger LOG = RgLog.getConsoleLogger(SQLMapperUtil.class.getName());
    private final static String DATABASE = "database";
    private final static String CORPORATION = "corporation";
    private final static String NODEATTR = "nodeAttrs";

    /**
     * 加载XML配置文件
     *
     * @param configFileName 配置文件名
     * @return 配置文件集合Map<key, value>
     * key：配置文件二级节点名
     * value:配置文件三级节点组成的json字符串
     * @throws DocumentException 异常
     */
    static Map<String, String> loadXmlConfigInfo(String configFileName) throws DocumentException {
        Document document = getDocunemt(configFileName);
        if (null != document) {
            Map<String, String> map = new ConcurrentHashMap<>();
            /* 根据document对象获取XML文件根节点 */
            Element rootElement = document.getRootElement();
            /* 通过element对象的elementIterator方法获取迭代器 */
            Iterator nodeIterator = rootElement.elementIterator();
            /* 遍历迭代器，获取根节点信息 */
            while (nodeIterator.hasNext()) {
                int index = 0;
                JSONArray jsonArray = new JSONArray();
                Element nodeElement = (Element) nodeIterator.next();
                /* 解析子节点信息 */
                Iterator childNode = nodeElement.elementIterator();
                while (childNode.hasNext()) {
                    Element childNodeElement = (Element) childNode.next();
                    JSONObject jsonElement = new JSONObject();
                    /* 将子节点信息拼成JSON串 */
                    String txt = PlaceholderResolver.resolve(childNodeElement.getText(), Collections.singletonList(PlaceholderResolver.getDefault()));
                    jsonElement.put(childNodeElement.getName(), txt);
                    /* 将子节点JSON串加载到JSON集合 */
                    jsonArray.add(index++, jsonElement);
                }
                /* 将子节点集合信息加载到MAP对象 */
                map.put(nodeElement.getName(), jsonArray.toJSONString());
            }
            return map;
        }
        return null;
    }

    public static void loadXmlConfig(String... configFileName) throws DocumentException {
        if (null == configFileName || 0 == Arrays.toString(configFileName).trim().length()) {
            LOG.error("未传入配置文件参数名!");
            throw new IllegalArgumentException();
        } else {
            for (String fileName : configFileName) {
                Document document = getDocunemt(fileName);
                if (null != document) {
                    /* 根据document对象获取XML文件根节点 */
                    Element rootElement = document.getRootElement();
                    /* 获取根节点的所有属性信息 */
                    List<Attribute> rootNodeAttrList = rootElement.attributes();
                    for (Attribute attribute : rootNodeAttrList) {
                        String value = PlaceholderResolver.resolve(attribute.getValue().trim(), Collections.singletonList(PlaceholderResolver.getDefault()));
                        attribute.setValue(value);
                    }
                    /* 将节点属性值全部加载到缓存 */
                    if (!rootNodeAttrList.isEmpty()) {
                        int iReturnCode = UnifiedCache.set(rootElement.getName().trim(), NODEATTR, rootNodeAttrList);
                        if (iReturnCode != 0) {
                            LOG.error("加载应用配置文件中的数据到缓存:【" + NODEATTR + "】失败!");
                            throw new IllegalArgumentException();
                        }
                    }
                    /* 通过element对象的elementIterator方法获取迭代器 */
                    Iterator nodeIterator = rootElement.elementIterator();
                    /* 遍历迭代器，获取根节点信息 */
                    while (nodeIterator.hasNext()) {
                        Element nodeElement = (Element) nodeIterator.next();
                        String nodeCashName = nodeElement.getName().trim();
                        /* 获取当前节点的所有属性信息 */
                        List<Attribute> nodeAttrList = nodeElement.attributes();
                        for (Attribute attribute : nodeAttrList) {
                            String value = PlaceholderResolver.resolve(attribute.getValue().trim(), Collections.singletonList(PlaceholderResolver.getDefault()));
                            attribute.setValue(value);
                        }
                        /* 数据库配置节点增加法人属性控制 */
                        if (nodeCashName.equals(DATABASE)) {
                            /* 获取当前节点的法人信息 */
                            if (!nodeAttrList.isEmpty()) {
                                Optional<Attribute> nodeAtrrOpt = nodeAttrList.stream().filter(item -> item.getName().equals(CORPORATION)).findFirst();
                                if (nodeAtrrOpt.isPresent() && !nodeAtrrOpt.get().getValue().trim().isEmpty()) {
                                    nodeCashName = nodeAtrrOpt.get().getValue().trim() + nodeCashName;
                                }
                            }
                        }
                        /* 清除子节点对应的缓存 */
                        UnifiedCache.clear(nodeCashName);
                        /* 将节点属性值全部加载到缓存 */
                        if (!nodeAttrList.isEmpty()) {
                            int iReturnCode = UnifiedCache.set(nodeCashName, NODEATTR, nodeAttrList);
                            if (iReturnCode != 0) {
                                LOG.error("加载应用配置文件中的数据到缓存:【" + NODEATTR + "】失败!");
                                throw new IllegalArgumentException();
                            }
                        }
                        /* 解析子节点信息 */
                        Iterator childNode = nodeElement.elementIterator();
                        while (childNode.hasNext()) {
                            Element childNodeElement = (Element) childNode.next();
                            Object object = UnifiedCache.get(nodeCashName, childNodeElement.getName().trim());
                            if (null == object) {
                                int iReturnCode = UnifiedCache.set(nodeCashName, childNodeElement.getName().trim(), PlaceholderResolver.resolve(childNodeElement.getText().trim(), Collections.singletonList(PlaceholderResolver.getDefault())));
                                if (iReturnCode != 0) {
                                    LOG.error("加载应用配置文件中的数据到缓存:【" + childNodeElement.getName().trim() + "】失败!");
                                    throw new IllegalArgumentException();
                                }
                                /* 获取当前子节点的所有属性信息 */
                                List<Attribute> childNodeAttrList = childNodeElement.attributes();
                                for (Attribute attribute : childNodeAttrList) {
                                    String value = PlaceholderResolver.resolve(attribute.getValue().trim(), Collections.singletonList(PlaceholderResolver.getDefault()));
                                    attribute.setValue(value);
                                }
                                /* 将子节点属性值全部加载到缓存 */
                                if (!childNodeAttrList.isEmpty()) {
                                    String childNodeAttrName = childNodeElement.getName().trim() + NODEATTR;
                                    iReturnCode = UnifiedCache.set(nodeCashName, childNodeAttrName, childNodeAttrList);
                                    if (iReturnCode != 0) {
                                        LOG.error("加载应用配置文件中的数据到缓存:【" + childNodeAttrName + "】失败!");
                                        throw new IllegalArgumentException();
                                    }
                                }
                            } else {
                                LOG.error("相同的配置重复加载：【" + childNodeElement.getName().trim() + "】【" + childNodeElement.getText().trim() + "】");
                                throw new IllegalArgumentException();
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * 基于文件节点获取缓存节点下面的参数值
     *
     * @param nodeName 节点名
     * @param key      节点参数名
     * @return 节点参数值
     */
    public static String getXmlConfigCacheKeyValue(String nodeName, String key) {
        if (null == nodeName || null == key) {
            LOG.error("文件节点【" + nodeName + "】或参数KEY:【" + key + "】为空!");
            return null;
        } else {
            return (String) UnifiedCache.get(nodeName.trim(), key.trim());
        }
    }

    /**
     * 基于文件节点获取对应节点的某个属性值
     *
     * @param nodeName 节点名
     * @param attrKey  属性KEY
     * @return 属性值
     */
    public static String getXmlConfigNodeAttrValue(String nodeName, String attrKey) {
        if (null == nodeName || null == attrKey) {
            LOG.error("文件节点【" + nodeName + "】或属性KEY:【" + attrKey + "】为空!");
            return null;
        } else {
            List<Attribute> nodeAttrList = (List<Attribute>) UnifiedCache.get(nodeName.trim(), NODEATTR);
            if (null != nodeAttrList && !nodeAttrList.isEmpty()) {
                Optional<Attribute> attributeOpt = nodeAttrList.stream().filter(item -> item.getName().trim().equals(attrKey.trim())).findFirst();
                return attributeOpt.map(Attribute::getValue).orElse(null);
            }
            return null;
        }
    }

    /**
     * 基于文件节点及子节点获取对应子节点的属性值
     *
     * @param nodeName      节点名
     * @param childNodeName 子节点名
     * @param attrKey       属性KEY
     * @return 属性值
     */
    public static String getXmlConfigChildNodeAttrValue(String nodeName, String childNodeName, String attrKey) {
        if (null == nodeName || null == childNodeName || null == attrKey) {
            LOG.error("文件节点【" + nodeName + "】或子节点【" + childNodeName + "】或属性KEY:【" + attrKey + "】为空!");
            return null;
        } else {
            List<Attribute> nodeAttrList = (List<Attribute>) UnifiedCache.get(nodeName.trim(), childNodeName.trim() + NODEATTR);
            if (null != nodeAttrList && !nodeAttrList.isEmpty()) {
                Optional<Attribute> attributeOpt = nodeAttrList.stream().filter(item -> item.getName().trim().equals(attrKey.trim())).findFirst();
                return attributeOpt.map(Attribute::getValue).orElse(null);
            }
            return null;
        }
    }

    /**
     * 获取所有数据库数据源的配置信息
     *
     * @param configFileName 配置文件
     * @return 数据库配置数据源信息
     * @throws DocumentException 异常
     */
    public static Set<String> getDbCorporationInfo(String... configFileName) throws DocumentException {
        Set<String> dbSet = new HashSet<>();
        if (null == configFileName || 0 == Arrays.toString(configFileName).trim().length()) {
            LOG.error("未传入配置文件参数名!");
            throw new IllegalArgumentException();
        }
        for (String fileName : configFileName) {
            Document document = getDocunemt(fileName);
            if (null != document) {
                /* 根据document对象获取XML文件根节点 */
                Element rootElement = document.getRootElement();
                /* 通过element对象的elementIterator方法获取迭代器 */
                Iterator nodeIterator = rootElement.elementIterator();
                /* 遍历迭代器，获取根节点信息 */
                while (nodeIterator.hasNext()) {
                    Element nodeElement = (Element) nodeIterator.next();
                    /* 获取与节点名匹配的节点信息 */
                    if (nodeElement.getName().trim().equals(DATABASE)) {
                        /* 获取当前节点的所有属性信息 */
                        List<Attribute> nodeAttrList = nodeElement.attributes();
                        /* 获取与节点属性名匹配的属性值 */
                        if (null != nodeAttrList && !nodeAttrList.isEmpty()) {
                            Optional<Attribute> nodeAtrrOpt = nodeAttrList.stream().filter(item -> item.getName().equals(CORPORATION)).findFirst();
                            if (nodeAtrrOpt.isPresent() && !nodeAtrrOpt.get().getValue().trim().isEmpty()) {
                                dbSet.add(PlaceholderResolver.resolve(nodeAtrrOpt.get().getValue().trim(), Collections.singletonList(PlaceholderResolver.getDefault())) + nodeElement.getName().trim());
                            } else {
                                dbSet.add(nodeElement.getName().trim());
                            }
                        } else {
                            dbSet.add(nodeElement.getName().trim());
                        }
                    }
                }
            }
        }
        return dbSet;
    }

    /**
     * 根据文件名和节点获取节点特定属性值
     *
     * @param configFileName 配置文件名
     * @param nodeName       节点名
     * @param attrName       节点属性名
     * @return 节点属性值
     */
    private static String getXmlConfigNodeAttrValue(String configFileName, String nodeName, String attrName) throws DocumentException {
        if (null == configFileName || 0 == configFileName.trim().length()) {
            LOG.error("未传入配置文件参数名!");
            throw new IllegalArgumentException();
        }
        Document document = getDocunemt(configFileName);
        if (null != document) {
            /* 根据document对象获取XML文件根节点 */
            Element rootElement = document.getRootElement();
            /* 通过element对象的elementIterator方法获取迭代器 */
            Iterator nodeIterator = rootElement.elementIterator();
            /* 遍历迭代器，获取根节点信息 */
            while (nodeIterator.hasNext()) {
                Element nodeElement = (Element) nodeIterator.next();
                /* 获取与节点名匹配的节点信息 */
                if (nodeElement.getName().trim().equals(nodeName)) {
                    /* 获取当前节点的所有属性信息 */
                    List<Attribute> nodeAttrList = nodeElement.attributes();
                    /* 获取与节点属性名匹配的属性值 */
                    if (null != nodeAttrList && !nodeAttrList.isEmpty()) {
                        Optional<Attribute> nodeAtrrOpt = nodeAttrList.stream().filter(item -> item.getName().equals(attrName)).findFirst();
                        if (nodeAtrrOpt.isPresent() && !nodeAtrrOpt.get().getValue().trim().isEmpty()) {
                            return PlaceholderResolver.resolve(nodeAtrrOpt.get().getValue().trim(), Collections.singletonList(PlaceholderResolver.getDefault()));
                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * 根据文件名和节点名获取节点值
     *
     * @param configFileName 配置文件名
     * @param nodeName       节点名
     * @return 节点值
     * @throws DocumentException 异常
     */
    public static String getXmlConfigValue(String configFileName, String nodeName) throws DocumentException {
        if (null == configFileName || 0 == configFileName.trim().length()) {
            LOG.error("未传入配置文件参数名!");
            throw new IllegalArgumentException();
        }
        Document document = getDocunemt(configFileName);
        if (null != document) {
            /* 根据document对象获取XML文件根节点 */
            Element rootElement = document.getRootElement();
            /* 通过element对象的elementIterator方法获取迭代器 */
            Iterator nodeIterator = rootElement.elementIterator();
            /* 遍历迭代器，获取根节点信息 */
            while (nodeIterator.hasNext()) {
                Element nodeElement = (Element) nodeIterator.next();
                if (nodeName.equals(nodeElement.getName())) {
                    return PlaceholderResolver.resolve(nodeElement.getText(), Collections.singletonList(PlaceholderResolver.getDefault()));
                }
            }
        }
        return null;
    }

    /**
     * 根据配置文件名、父节点及子节点包获取子节点值
     *
     * @param configFileName 配置文件名
     * @param nodeName       父节点名
     * @param keyName        子节点名
     * @return 子节点值
     * @throws DocumentException 异常
     */
    public static String getXmlConfigValue(String configFileName, String nodeName, String keyName) throws DocumentException {
        if (null == configFileName || 0 == configFileName.trim().length()) {
            LOG.error("未传入配置文件参数名!");
            throw new IllegalArgumentException();
        }
        Document document = getDocunemt(configFileName);
        if (null != document) {
            /* 根据document对象获取XML文件根节点 */
            Element rootElement = document.getRootElement();
            /* 通过element对象的elementIterator方法获取迭代器 */
            Iterator nodeIterator = rootElement.elementIterator();
            /* 遍历迭代器，获取根节点信息 */
            while (nodeIterator.hasNext()) {
                Element nodeElement = (Element) nodeIterator.next();
                if (nodeName.equals(nodeElement.getName())) {
                    /* 解析子节点信息 */
                    Iterator childNode = nodeElement.elementIterator();
                    while (childNode.hasNext()) {
                        Element childNodeElement = (Element) childNode.next();
                        if (keyName.equals(childNodeElement.getName())) {
                            return PlaceholderResolver.resolve(childNodeElement.getText(), Collections.singletonList(PlaceholderResolver.getDefault()));
                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * 获取文件document对象
     *
     * @param configFileName 配置文件名
     * @return document 文档
     * @throws DocumentException 异常
     */
    private static Document getDocunemt(String configFileName) throws DocumentException {
        SAXReader reader = new SAXReader();
        /* 判断文件是否存在 */
        File file = new File(configFileName);
        if (file.exists()) {
            /*  通过reader对象的read方法加载XML文件,获取docuemnt对象 */
            return reader.read(new File(configFileName));
        }
        return null;
    }
}
